# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\gevent\os.py
"""
Low-level operating system functions from :mod:`os`.

Cooperative I/O
===============

This module provides cooperative versions of :func:`os.read` and
:func:`os.write`. These functions are *not* monkey-patched; you
must explicitly call them or monkey patch them yourself.

POSIX functions
---------------

On POSIX, non-blocking IO is available.

- :func:`nb_read`
- :func:`nb_write`
- :func:`make_nonblocking`

All Platforms
-------------

On non-POSIX platforms (e.g., Windows), non-blocking IO is not
available. On those platforms (and on POSIX), cooperative IO can
be done with the threadpool.

- :func:`tp_read`
- :func:`tp_write`

Child Processes
===============

The functions :func:`fork` and (on POSIX) :func:`forkpty` and :func:`waitpid` can be used
to manage child processes.

.. warning::

   Forking a process that uses greenlets does not eliminate all non-running
   greenlets. Any that were scheduled in the hub of the forking thread in the parent
   remain scheduled in the child; compare this to how normal threads operate. (This behaviour
   may change is a subsequent major release.)
"""
from __future__ import absolute_import
import os, sys
from gevent.hub import get_hub, reinit
from gevent._compat import PY3
from gevent._util import copy_globals
import errno

EAGAIN = getattr(errno, "EAGAIN", 11)
try:
    import fcntl
except ImportError:
    fcntl = None

__implements__ = ["fork"]
__extensions__ = ["tp_read", "tp_write"]
_read = os.read
_write = os.write
ignored_errors = [EAGAIN, errno.EINTR]
if fcntl:
    __extensions__ += ["make_nonblocking", "nb_read", "nb_write"]

    def make_nonblocking(fd):
        """Put the file descriptor *fd* into non-blocking mode if possible.
        
        :return: A boolean value that evaluates to True if successful."""
        flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
        if not bool(flags & os.O_NONBLOCK):
            fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
            return True

    def nb_read(fd, n):
        """Read up to `n` bytes from file descriptor `fd`. Return a string
        containing the bytes read. If end-of-file is reached, an empty string
        is returned.
        
        The descriptor must be in non-blocking mode.
        """
        hub, event = (None, None)
        while True:
            try:
                return _read(fd, n)
            except OSError as e:
                if e.errno not in ignored_errors:
                    raise
                if not PY3:
                    sys.exc_clear()

            if hub is None:
                hub = get_hub()
                event = hub.loop.io(fd, 1)
            hub.wait(event)

    def nb_write(fd, buf):
        """Write bytes from buffer `buf` to file descriptor `fd`. Return the
        number of bytes written.
        
        The file descriptor must be in non-blocking mode.
        """
        hub, event = (None, None)
        while True:
            try:
                return _write(fd, buf)
            except OSError as e:
                if e.errno not in ignored_errors:
                    raise
                if not PY3:
                    sys.exc_clear()

            if hub is None:
                hub = get_hub()
                event = hub.loop.io(fd, 2)
            hub.wait(event)


def tp_read(fd, n):
    """Read up to *n* bytes from file descriptor *fd*. Return a string
    containing the bytes read. If end-of-file is reached, an empty string
    is returned.
    
    Reading is done using the threadpool.
    """
    return get_hub().threadpool.apply(_read, (fd, n))


def tp_write(fd, buf):
    """Write bytes from buffer *buf* to file descriptor *fd*. Return the
    number of bytes written.
    
    Writing is done using the threadpool.
    """
    return get_hub().threadpool.apply(_write, (fd, buf))


if hasattr(os, "fork"):
    _raw_fork = os.fork

    def fork_gevent():
        """
        Forks the process using :func:`os.fork` and prepares the
        child process to continue using gevent before returning.
        
        .. note::
        
            The PID returned by this function may not be waitable with
            either the original :func:`os.waitpid` or this module's
            :func:`waitpid` and it may not generate SIGCHLD signals if
            libev child watchers are or ever have been in use. For
            example, the :mod:`gevent.subprocess` module uses libev
            child watchers (which parts of gevent use libev child
            watchers is subject to change at any time). Most
            applications should use :func:`fork_and_watch`, which is
            monkey-patched as the default replacement for
            :func:`os.fork` and implements the ``fork`` function of
            this module by default, unless the environment variable
            ``GEVENT_NOWAITPID`` is defined before this module is
            imported.
        
        .. versionadded:: 1.1b2
        """
        result = _raw_fork()
        if not result:
            reinit()
        return result

    def fork():
        """
        A wrapper for :func:`fork_gevent` for non-POSIX platforms.
        """
        return fork_gevent()

    if hasattr(os, "forkpty"):
        _raw_forkpty = os.forkpty

        def forkpty_gevent():
            """
            Forks the process using :func:`os.forkpty` and prepares the
            child process to continue using gevent before returning.
            
            Returns a tuple (pid, master_fd). The `master_fd` is *not* put into
            non-blocking mode.
            
            Availability: Some Unix systems.
            
            .. seealso:: This function has the same limitations as :func:`fork_gevent`.
            
            .. versionadded:: 1.1b5
            """
            pid, master_fd = _raw_forkpty()
            if not pid:
                reinit()
            return (pid, master_fd)

        forkpty = forkpty_gevent
        __implements__.append("forkpty")
        __extensions__.append("forkpty_gevent")
    if hasattr(os, "WNOWAIT") or hasattr(os, "WNOHANG"):
        import time

        _waitpid = os.waitpid
        _WNOHANG = os.WNOHANG
        _on_child_hook = lambda: None
        _watched_children = {}

        def _on_child(watcher, callback):
            watcher.stop()
            _watched_children[watcher.pid] = (watcher.pid, watcher.rstatus, time.time())
            if callback:
                callback(watcher)
            _on_child_hook()
            _reap_children()

        def _reap_children(timeout=60):
            now = time.time()
            oldest_allowed = now - timeout
            dead = [
                pid
                for pid, val in _watched_children.items()
                if isinstance(val, tuple)
                if val[2] < oldest_allowed
            ]
            for pid in dead:
                del _watched_children[pid]

        def waitpid(pid, options):
            """
            Wait for a child process to finish.
            
            If the child process was spawned using
            :func:`fork_and_watch`, then this function behaves
            cooperatively. If not, it *may* have race conditions; see
            :func:`fork_gevent` for more information.
            
            The arguments are as for the underlying
            :func:`os.waitpid`. Some combinations of *options* may not
            be supported cooperatively (as of 1.1 that includes
            WUNTRACED). Using a *pid* of 0 to request waiting on only processes
            from the current process group is not cooperative.
            
            Availability: POSIX.
            
            .. versionadded:: 1.1b1
            .. versionchanged:: 1.2a1
               More cases are handled in a cooperative manner.
            """
            if pid <= 0:
                if pid == -1:
                    for k, v in _watched_children.items():
                        if isinstance(v, tuple):
                            pid = k
                            break

                if pid <= 0:
                    if pid == -1:
                        if options == 0:
                            hub = get_hub()
                            watcher = hub.loop.child(0, False)
                            hub.wait(watcher)
                            return (watcher.rpid, watcher.rstatus)
                        return _waitpid(pid, options)
                    if pid in _watched_children:
                        if options & _WNOHANG or isinstance(
                            _watched_children[pid], tuple
                        ):
                            result = _watched_children[pid]
                            if isinstance(result, tuple):
                                del _watched_children[pid]
                                return result[:2]
                            return (0, 0)
                        watcher = _watched_children[pid]
                        new_watcher = watcher.loop.child(pid, False)
                        get_hub().wait(new_watcher)
                        return (watcher.rpid, watcher.rstatus)
                    return _waitpid(pid, options)

        def fork_and_watch(callback=None, loop=None, ref=False, fork=fork_gevent):
            """
            Fork a child process and start a child watcher for it in the parent process.
            
            This call cooperates with :func:`waitpid` to enable cooperatively waiting
            for children to finish. When monkey-patching, these functions are patched in as
            :func:`os.fork` and :func:`os.waitpid`, respectively.
            
            In the child process, this function calls :func:`gevent.hub.reinit` before returning.
            
            Availability: POSIX.
            
            :keyword callback: If given, a callable that will be called with the child watcher
                when the child finishes.
            :keyword loop: The loop to start the watcher in. Defaults to the
                loop of the current hub.
            :keyword fork: The fork function. Defaults to :func:`the one defined in this
                module <gevent.os.fork_gevent>` (which automatically calls :func:`gevent.hub.reinit`).
                Pass the builtin :func:`os.fork` function if you do not need to
                initialize gevent in the child process.
            
            .. versionadded:: 1.1b1
            .. seealso::
                :func:`gevent.monkey.get_original` To access the builtin :func:`os.fork`.
            """
            pid = fork()
            if pid:
                loop = loop or get_hub().loop
                watcher = loop.child(pid, ref=ref)
                _watched_children[pid] = watcher
                watcher.start(_on_child, watcher, callback)
            return pid

        __extensions__.append("fork_and_watch")
        __extensions__.append("fork_gevent")
        if "forkpty" in __implements__:

            def forkpty_and_watch(
                callback=None, loop=None, ref=False, forkpty=forkpty_gevent
            ):
                """
                Like :func:`fork_and_watch`, except using :func:`forkpty_gevent`.
                
                Availability: Some Unix systems.
                
                .. versionadded:: 1.1b5
                """
                result = []

                def _fork():
                    pid_and_fd = forkpty()
                    result.append(pid_and_fd)
                    return pid_and_fd[0]

                fork_and_watch(callback, loop, ref, _fork)
                return result[0]

            __extensions__.append("forkpty_and_watch")
        if not os.getenv("GEVENT_NOWAITPID"):

            def fork(*args, **kwargs):
                """
                Forks a child process and starts a child watcher for it in the
                parent process so that ``waitpid`` and SIGCHLD work as expected.
                
                This implementation of ``fork`` is a wrapper for :func:`fork_and_watch`
                when the environment variable ``GEVENT_NOWAITPID`` is *not* defined.
                This is the default and should be used by most applications.
                
                .. versionchanged:: 1.1b2
                """
                return fork_and_watch(*args, **kwargs)

            if "forkpty" in __implements__:

                def forkpty(*args, **kwargs):
                    """
                    Like :func:`fork`, but using :func:`forkpty_gevent`.
                    
                    This implementation of ``forkpty`` is a wrapper for :func:`forkpty_and_watch`
                    when the environment variable ``GEVENT_NOWAITPID`` is *not* defined.
                    This is the default and should be used by most applications.
                    
                    .. versionadded:: 1.1b5
                    """
                    return forkpty_and_watch(*args, **kwargs)

            __implements__.append("waitpid")
        else:

            def fork():
                """
                Forks a child process, initializes gevent in the child,
                but *does not* prepare the parent to wait for the child or receive SIGCHLD.
                
                This implementation of ``fork`` is a wrapper for :func:`fork_gevent`
                when the environment variable ``GEVENT_NOWAITPID`` *is* defined.
                This is not recommended for most applications.
                """
                return fork_gevent()

            if "forkpty" in __implements__:

                def forkpty():
                    """
                    Like :func:`fork`, but using :func:`os.forkpty`
                    
                    This implementation of ``forkpty`` is a wrapper for :func:`forkpty_gevent`
                    when the environment variable ``GEVENT_NOWAITPID`` *is* defined.
                    This is not recommended for most applications.
                    
                    .. versionadded:: 1.1b5
                    """
                    return forkpty_gevent()

            __extensions__.append("waitpid")
    else:
        __implements__.remove("fork")
    __imports__ = copy_globals(
        os,
        globals(),
        names_to_ignore=__implements__ + __extensions__,
        dunder_names_to_keep=(),
    )
    __all__ = list(set(__implements__ + __extensions__))
